import os
import socket
import time, re
import struct
from operator import xor

# v2 protocol client
class Packet :
	def __init__(self, host, port, logfile) :
		self.sock = self.connect(host, port)
		self.packet = ""
		self.maps = {}

		self.command = None
		self.start_time = ""
		self.end_time = ""
		self.readCommand = None

		if (os.path.exists(logfile)) : self.fd = open(logfile, "a")
		else : self.fd = open(logfile, "w")
	
	def __del__(self) :
		self.close()

	def close(self) :
		if (self.sock != None) :
			try :
				self.sock.close()
				self.sock = None
			except :
				pass

	def connect(self, host, port) :
		sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		sock.connect((host, int(port)))
		return sock

	def write_log(self) :
		log = "%s	send_command	0X%x	read_command	0X%x	start %s	end %s	elapsed %.4f" % (time.strftime("%Y/%m/%d", time.localtime(self.start_time)), \
																self.command, \
																self.readCommand, \
																time.strftime("%H:%M:%S", time.localtime(self.start_time)), \
																time.strftime("%H:%M:%S", time.localtime(self.end_time)), \
																float(self.end_time - self.start_time) )
																
		#log = "%s	command	0x%x	elapsed	%.4f" % (time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(self.start_time)), \
		#											self.command, \
		#											float(self.end_time - self.start_time)
		#											)
		self.fd.write(log + "\n")
		self.fd.flush()
	
	def read_msg(self):
		self.packet += self.sock.recv(8192)

		self.end_time = time.time()
		

		plen = len(self.packet)
		while (plen >= 6):
			fmt = '<BH{:d}s'.format(plen-3)
			head, bodylen, body = struct.unpack(fmt, self.packet)
			start, cmd, data = 0, 0, ''
			if (bodylen == 3):
				remain = plen-3 - bodylen
				if remain < 0:
					# need more data
					return
				elif (remain > 0):
					fmt = '<BH{:d}s'.format(remain)
					start, cmd, next = struct.unpack(fmt, body)
					self.packet = next
				else:
					start, cmd = struct.unpack('<BH', body)
					self.packet = ''
			else:
				remain = plen-3 - bodylen
				if (remain < 0):
					# need more data
					return
				elif (remain > 0):
					fmt = '<BH{:d}s{:d}s'.format(bodylen-3, remain)
					start, cmd, data, next = struct.unpack(fmt, body)
					self.packet = next
				else:
					fmt = '<BH{:d}s'.format(bodylen-3)
					start, cmd, data = struct.unpack(fmt, body)
					self.packet = ''
			handler = self.maps.get(cmd)
			self.readCommand = cmd
			self.write_log()
			if (handler): handler(data)
			plen = len(self.packet)
	
	def add_command(self, cmd, handler):
		if self.maps is None:
			self.maps = []
		self.maps[cmd] = handler
	
	def send_data(self, cmd, data):
		datalen = len(data)
		if datalen == 0:
			packet = struct.pack('<BHBH',
				0xff, datalen+3, 0x01, cmd)
		else:
			fmt = '<BHBH{:d}s'.format(datalen)
			packet = struct.pack(fmt,
				0xff, datalen+3, 0x01, cmd, data)

		self.command = cmd
		self.start_time = time.time()

		self.sock.sendall(packet)

	
	def send_msg(self, cmd, encryptKey, fmt, *arg):
		msg = build_msg(fmt, *arg)

		if (encryptKey) :
			msg = self.xorEncode(msg, encryptKey)

		self.send_data(cmd, msg)

	def xorEncode(self, data, key) :
		keyLen = len(key)
		keyIndex = 0
		xorResult = ""

		for i in range(0, len(data)) :
			xorResult += chr(xor(ord(data[i]), ord(key[keyIndex])))
			keyIndex += 1
			if (keyIndex >= keyLen) : keyIndex = 0

		return xorResult


# message builder / parser
def build_msg(fmt, *arg):
	msg = ''
	idx = 0
	for x in fmt.split('%'):
		xx = re.findall('([0-9]*)([A-Za-z]+)', x)
		for num,t in xx:
			func = build_disp.get(t)
			if func:
	#			print "num(%s), val(%s)" % (num, arg[idx])
				msg += func(num, arg[idx])
				idx = idx+1
			else:
				msg_none(t, num)
				return
	return msg

def parse_msg(fmt, data):
	result = []
	for x in fmt.split('%'):
		xx = re.findall('([0-9]*)([A-Za-z]+)', x)
		for num,t in xx:
			func = parse_disp.get(t)
			if func:
				part, data = func(num, data)
				result.append(part)
			else:
				pmsg_none(t, num)
				return
	return result

def repeat_str(s, cnt):
	d, m = divmod(cnt, len(s))
	return s * d + s[:m]

def msg_char(size, val):
	return struct.pack('b', val)

def msg_uchar(size, val):
	return struct.pack('B', val)

def msg_decimal(size, val):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)
	
	if size == 2:
		return struct.pack('<h', val)
	elif size == 0 or size == 4:
		return struct.pack('<l', val)
	elif size == 8:
		return struct.pack('<q', val)
	else:
		print "uknown decimal size", size
		assert False, "uknown decimal size"

def msg_udecimal(size, val):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)
	
	if size == 2:
		return struct.pack('<H', val)
	elif size == 0 or size == 4:
		return struct.pack('<L', val)
	elif size == 8:
		return struct.pack('<Q', val)
	else:
		print "uknown decimal size", size
		assert False, "uknown decimal size"

def msg_string(size, val):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)

	vlen = len(val)
	if size > vlen:
		zlen = size - vlen
		return struct.pack('<H{:d}s{:d}s'.format(vlen, zlen),
			size, val, repeat_str('\x00', zlen))
	else:
		return struct.pack('<H{:d}s'.format(vlen), vlen, val)

def msg_fixed_string(size, val):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)
	
	vlen = len(val)
	if size > vlen:
		zlen = size - vlen
		return struct.pack('<{:d}s{:d}s'.format(vlen, zlen),
			val, repeat_str('\x00', zlen))
	else:
		return struct.pack('<{:d}s'.format(vlen), val)

def msg_float(size, val):
	return struct.pack('<f', val)

def msg_double(size, val):
	return struct.pack('<d', val)

def msg_none(msg_type, size):
	print msg_type
	assert False, "unknown message type"

build_disp = {
	'c': msg_char,
	'C': msg_uchar,
	'd': msg_decimal,
	'D': msg_udecimal,
	's': msg_fixed_string,
	'S': msg_string,
	'f': msg_float,
	'g': msg_double, }
	
def pmsg_char(size, data):
	return struct.unpack('b{:d}s'.format(len(data)-1), data)

def pmsg_uchar(size, data):
	return struct.unpack('B{:d}s'.format(len(data)-1), data)

def pmsg_decimal(size, data):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)

	if size == 2:
		return struct.unpack('<h{:d}s'.format(len(data)-2), data)
	elif size == 0 or size == 4:
		return struct.unpack('<l{:d}s'.format(len(data)-4), data)
	elif size == 8:
		return struct.unpack('<q{:d}s'.format(len(data)-8), data)
	else:
		print size
		assert False, "unknown decimal size"

def pmsg_udecimal(size, data):
	if len(size) == 0:
		size = 0
	else:
		size = int(size)
	
	if size == 2:
		return struct.unpack('<H{:d}s'.format(len(data)-2), data)
	elif size == 0 or size == 4:
		return struct.unpack('<L{:d}s'.format(len(data)-4), data)
	elif size == 8:
		return struct.unpack('<Q{:d}s'.format(len(data)-8), data)
	else:
		print size
		assert False, "unknown decimal size"

def pmsg_string(size, data):
	if len(size) == 0:
		size = 2147483647
	else:
		size = int(size)

	slen, data = struct.unpack('<H{:d}s'.format(len(data)-2), data)
	if size >= slen:
		return struct.unpack('{:d}s{:d}s'.format(slen, len(data)-slen), data)
	else:
		s, skip, r = struct.unpack('{:d}s{:d}s{:d}s'.format(size,
			slen-size, len(data)-slen), data)
		return s, r

def pmsg_fixed_string(size, data):
	if len(size) == 0:
		assert False, "unknown fixed string size"
	else:
		size = int(size)

	slen = len(data)

	if size >= slen:
		return struct.unpack('{:d}s{:d}s'.format(slen, len(data)-slen), data)
	else:
		s, skip, r = struct.unpack('{:d}s{:d}s{:d}s'.format(size,
			slen-size, len(data)-slen), data)
		return s, skip

def pmsg_float(size, data):
	return struct.unpack('<f{:d}s'.format(len(data)-4), data)

def pmsg_double(size, data):
	return struct.unpack('<d{:d}s'.format(len(data)-8), data)

def pmsg_none(msg_type, size):
	print msg_type
	assert False, "unknown message type"

parse_disp = {
	'c': pmsg_char,
	'C': pmsg_uchar,
	'd': pmsg_decimal,
	'D': pmsg_udecimal,
	's': pmsg_fixed_string,
	'S': pmsg_string,
	'f': pmsg_float,
	'g': pmsg_double, }
